//
//  BC_ParserStringGame.m
//  CocOth
//
//  Created by BrunoCausse on 06/12/10.
//  Copyright 2010-2013 poulailler cie. All rights reserved.
//

#import "BC_ParserStringGame.h"

#import "BC_Board.h"
#import "BC_Move.h"


//Methodes privées
@interface BC_ParserStringGame(PrivateAPI) 

- (int)findType;
- (NSString *)ggfValueForKey:(NSString *)key;
- (NSRange)rangeOfNextMoveAtIndex:(int)index;
- (BC_Move *)moveWithRange:(NSRange)r;

- (NSString *)pgnValueForKey:(NSString *)key;

@end



@implementation BC_ParserStringGame

@synthesize stringGame;
@synthesize type;

#pragma mark init/dealloc

- (id)initWithString:(NSString *)game
{
	self = [super init];
	if(self) {
		[self setStringGame:[game stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
		type = [self findType];
	}
	return self;
}

- (void)dealloc
{
	[self setStringGame:nil];
	[super dealloc];
}

#pragma mark public methods

- (NSString *)event
{
	if (type == pgnType) {
		
		return [self pgnValueForKey:@"[Event"];
		
	}
	
	return @"";
}

- (NSString *)site
{
	if(type == ggfType) {
		
		return [self ggfValueForKey:@"PC["];
		
	} else if (type == pgnType) {
		
		return [self pgnValueForKey:@"[Site"];
		
	}
	
	return @"";
}

- (NSString *)date
{
	if(type == ggfType) {
		
		return [self ggfValueForKey:@"DT["];
		
	} else if (type == pgnType) {
		
		return [self pgnValueForKey:@"[Date"];
		
	}
	
	return @"";
}


- (NSString *)playerBlack
{
	if(type == ggfType) {
		
		return [self ggfValueForKey:@"PB["];
		
	} else if (type == pgnType) {
		
		return [self pgnValueForKey:@"[Black"];
		
	}
	
	return @"";
}

- (NSString *)playerWhite
{
	if(type == ggfType) {
		
		return [self ggfValueForKey:@"PW["];
		
	} else if (type == pgnType) {
		
		return [self pgnValueForKey:@"[White"];
		
	}
	
	return @"";
}

- (NSString *)result
{
	if(type == ggfType) {
		
		return [self ggfValueForKey:@"RE["];
		
	} else if (type == pgnType) {
		
		return [self pgnValueForKey:@"[Result"];
		
	}
	
	return @"";
}
 
- (NSString *)board
{
	if(type == ggfType) {
		
		NSString *board = [self ggfValueForKey:@"BO[8"];
		NSArray *line   = [board componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
		return [line componentsJoinedByString:@" "];
		
	} else if (type == pgnType) {
		
		return @"[-------- -------- -------- ---OX--- ---XO--- -------- -------- -------- X]";
		
	} else if (type == boardType) {
		
		return stringGame;
		
	} else if (listMovesType) {
		
		return @"[-------- -------- -------- ---OX--- ---XO--- -------- -------- -------- X]";
	}
	
	return @"";
}


- (NSArray *)listMoves
{
	
	NSMutableArray *list = nil;
	
	if (type != unknownType) {
		
		list = [NSMutableArray arrayWithCapacity:64]; //autorelease
		
		BC_Board *board = [[BC_Board alloc] initWithString:[self board]];
		
		NSString *listMove = [stringGame uppercaseString];
		
		if (type == ggfType) {
			
			NSRange r = [stringGame rangeOfString:@"BO["];
			
			do {
				r = [self rangeOfNextMoveAtIndex:r.location];
				
				if(r.location != NSNotFound) {

					BC_Move *move = [self moveWithRange:r];
					
					if ([move positionAtIndex:0] == PASS && [board shouldPass]) {
						
						[list addObject:move];
						
						[board doPass];
						
					} else if ([board isLegal:[move positionAtIndex:0]]) {

						[board generateMove:move];
						
						[list addObject:move];
						
						[board doMove:move];
						
					}

				}
					
					
					
			} while (r.location != NSNotFound);
													  
			
		} else {
			
			//Try to parse list moves
			
			for(int index = 0; index<([listMove length]-1); index++) {
				unichar abscise = [listMove characterAtIndex:index];
				if (abscise == 'A' ||
					abscise == 'B' ||
					abscise == 'C' ||
					abscise == 'D' ||
					abscise == 'E' ||
					abscise == 'F' ||
					abscise == 'G' ||
					abscise == 'H') {
					
					unichar ordonne = [listMove characterAtIndex:(index+1)];
					if(ordonne == '1' ||
					   ordonne == '2' ||
					   ordonne == '3' ||
					   ordonne == '4' ||
					   ordonne == '5' ||
					   ordonne == '6' ||
					   ordonne == '7' ||
					   ordonne == '8') {
						
						int square = [BC_Move stringToIndex:[NSString stringWithFormat:@"%c%c", abscise, ordonne]];
						
						if ([board isLegal:square]) {
							BC_Move *move = [[BC_Move alloc] initWithLocation:square];
							[board generateMove:move];
							//lecture du score/time
							
							[list addObject:move];
							
							[board doMove:move];
							
							if ([board shouldPass]) {
								[board doPass];
								if (![board shouldPass]) {
									BC_Move *pass = [[BC_Move alloc] initWithLocation:PASS];
									[list addObject:pass];
									[pass release];
								} else {
									[board doPass];
								}
								
							}
							
							[move release];
							
						}
					}
				}
			}
		}
		
		
		[board release];
	}
	
	return list;
	
}

#pragma mark private methods

- (int)findType
{
    //ligne vide
    if(stringGame == nil || [stringGame isEqual:@"\n"] || [stringGame length] == 0)
        return unknownType;
        
	/* type ggf
	 Conditions :
		commence par "(;"
		fini par ";)"
		au moins cette balise "GM[Othello]"
	 */
    	
	if ([stringGame hasPrefix:@"(;"] && [stringGame hasSuffix:@";)"]) {
		NSRange r = [stringGame rangeOfString:@"GM[Othello]"];
		if(r.location != NSNotFound)
			return ggfType;
	}
	
	/* type pgn
	 Conditions:
	 balise "[Event "
	 balise "[Site "
	 balise "[Date "
	 balise "[Round "
	 balise "[Black "
	 balise "[White "
	 balise "[result "
	 */
	
	NSRange r = [stringGame rangeOfString:@"[Event "];
	if(r.location != NSNotFound) {
		r = [stringGame rangeOfString:@"[Site "];
		if(r.location != NSNotFound) {
			r = [stringGame rangeOfString:@"[Date "];
			if(r.location != NSNotFound) {
				r = [stringGame rangeOfString:@"[Round "];
				if(r.location != NSNotFound) {
					r = [stringGame rangeOfString:@"[Black "];
					if(r.location != NSNotFound) {
						r = [stringGame rangeOfString:@"[White "];
						if(r.location != NSNotFound) {
							r = [stringGame rangeOfString:@"[Result "];
							if(r.location != NSNotFound) {
								return pgnType;
							}
						}
					}
				}
			}
		}
	}
	
	/* type board
	 condition
	 BC_Board valid
	 */
	
	BC_Board *board = nil;
	@try {
		board = [[BC_Board alloc] initWithString:stringGame];
	}
	@catch (NSException * e) {
		board = nil;
	}
	@finally {
		[board autorelease];
	}
	
	if (board != nil)
		return boardType;
	

	/* type listMoves
	 condition au moins un move valide
	 */

	board = [[BC_Board alloc] init];
	NSString *listMove = [stringGame uppercaseString];
	for(int index = 0; index<([listMove length]-1); index++) {
		unichar abscise = [listMove characterAtIndex:index];
		if (abscise == 'A' ||
			abscise == 'B' ||
			abscise == 'C' ||
			abscise == 'D' ||
			abscise == 'E' ||
			abscise == 'F' ||
			abscise == 'G' ||
			abscise == 'H') {
			
			unichar ordonne = [listMove characterAtIndex:(index+1)];
			if(ordonne == '1' ||
			   ordonne == '2' ||
			   ordonne == '3' ||
			   ordonne == '4' ||
			   ordonne == '5' ||
			   ordonne == '6' ||
			   ordonne == '7' ||
			   ordonne == '8') {
				
				int square = [BC_Move stringToIndex:[NSString stringWithFormat:@"%c%c", abscise, ordonne]];
				
				if ([board isLegal:square]) {
					[board release];
					return listMovesType;
				}
			}
		}
	}
	
	[board release];

		
	return unknownType;
}


- (NSString *)ggfValueForKey:(NSString *)key
{
	//find key
	NSRange r1 = [stringGame rangeOfString:key];
	if(r1.location != NSNotFound) {
		//find "]"
		NSRange r2 = [stringGame rangeOfString:@"]" options:NSLiteralSearch range:NSMakeRange(r1.location, [stringGame length]-r1.location)];
		if(r2.location != NSNotFound) {
			return [stringGame substringWithRange:NSMakeRange(r1.location+r1.length, r2.location-(r1.location+r1.length))];
		}
	}
	
	return @"";
}

- (NSString *)pgnValueForKey:(NSString *)key
{
	//find key
	NSRange r1 = [stringGame rangeOfString:key];
	if(r1.location != NSNotFound) {
		//find first "
		r1 = [stringGame rangeOfString:@"\"" options:NSLiteralSearch range:NSMakeRange(r1.location, [stringGame length]-r1.location)];
		if(r1.location != NSNotFound) {
			//find second "
			NSRange r2 = [stringGame rangeOfString:@"\"" options:NSLiteralSearch range:NSMakeRange(r1.location+1, [stringGame length]-(r1.location+1))];

			return [stringGame substringWithRange:NSMakeRange(r1.location+1, r2.location-(r1.location+1))];
		}
	}
	
	return @"";
}

- (NSRange)rangeOfNextMoveAtIndex:(int)index
{
	NSRange r  = NSMakeRange(index, [stringGame length]-index);
	NSRange r1 = [stringGame rangeOfString:@"B[" options:NSLiteralSearch range:r];
	NSRange r2 = [stringGame rangeOfString:@"W[" options:NSLiteralSearch range:r];
	
	if (r1.location != NSNotFound) {
		if (r2.location != NSNotFound && r1.location > r2.location) {
				r1 = r2;
		}
	} else {
		if (r2.location != NSNotFound) {
			r1 = r2;
		}
	}
	
	if (r1.location == NSNotFound)
		return r1;

	//find "]"
	r2 = [stringGame rangeOfString:@"]" options:NSLiteralSearch range:NSMakeRange(r1.location, [stringGame length]-r1.location)];
	if(r2.location != NSNotFound)
		return NSMakeRange(r1.location+r1.length, r2.location-(r1.location+r1.length));
	
	return r2;
}

- (BC_Move *)moveWithRange:(NSRange)r
{
	NSArray *components = [[stringGame substringWithRange:r] componentsSeparatedByString:@"/"];
	int square = [BC_Move stringToIndex:[[components objectAtIndex:0] uppercaseString]];
	BC_Move *move = [[BC_Move alloc] initWithLocation:square];
	
	//les attributs
	if ([components count] > 1) { //attention la sentinelle nil compte
		//le score
		NSString *score = [components objectAtIndex:1];
		if(![score isEqualToString:@""])
			[move setScore:[score floatValue]];
		if ([components count] > 2) {  //attention la sentinelle nil compte
			//le temps
			NSString *time = [components objectAtIndex:2];
			if(![time isEqualToString:@""])
				[move setTime:[time floatValue]];
		}
	}
	
	return [move autorelease];
}


@end
